from bs4 import BeautifulSoup
html_doc = """
<html><head><title>index</title></head>
<body>
<p class="title"><b>首页</b></p>
<p class="main">我常用的网站
<a href="https://www.google.com" class="website" id="google">Google</a>
<a href="https://www.baidu.com" class="website" id="baidu">Baidu</a>
<a href="https://cn.bing.com" class="website" id="bing">Bing</a>
</p>
<div><!--这是注释内容--></div>
<p class="content1">...</p>
<p class="content2">...</p>
</body>
"""
soup = BeautifulSoup(html_doc, "lxml")

# TODO 过滤器
# demo 1 根据 TAG 的 name 来查找标签，下面的例子会查找文档中的所有 b 标签。同时要注意统一传入 Unicode 编码以避免 Beautiful Soup 解析编码出错。
# tags = soup.fin
# demo 2 如果传入正则表达式作为参数，那么 Beautiful Soup 会通过正则表达式的 match() 来匹配内容。
# import re
# for tag in soup.find_all(re.compile("^b")):
#     print(tag.name)
# demo 3 如果传入列表参数，那么 Beautiful Soup 会将与列表中任一一个元素匹配的内容返回。
# for tag in soup.find_all(['a', 'b']):
#     print(tag)
# demo 4 True 可以匹配任何值，下面的例子是查找所有的 TAG 但不会返回字符串。
# for tag in soup.find_all(True):
#     print(tag.name, end=', ')
# demo 5 方法。我们可以定义一个方法，该方法只接受一个参数，若该方法返回 True 则表示当前元素匹配并且被找到，返回 False 意味着没找到。下面的例子展示了查找所有同时包含 class 属性和 id 属性的节点。
# def has_id_class(tag):
#     return tag.has_attr('id') and tag.has_attr('class')
#
# tags = soup.find_all(has_id_class)
# for tag in tags:
# 	print(tag)
# TODO find_all() 函数
# demo 6 如果我们传入 find_all() 函数不是搜索内置的参数名，那么搜索是就会将该参数对应到属性上去。下文的例子表示查找 id 为 google 的节点
# tags = soup.find_all(id='google')
# print(tags[0]['href'])
# for tag in soup.find_all(id=True): # 查找所有包含 id 属性的 TAG
# 	print(tag['href'])

# demo 7 按照 CSS 类名搜索，但是标识 CSS 的关键字 class 在 Python 中是内置关键字，从 Beautiful Soup 4.1.1 版本开始，可以通过 class_ 参数搜索有指定 CSS 类名的 TAG：
# tags = soup.find_all("a", class_="website")
# for tag in tags:
# 	print(tag['href'])
#
# def has_seven_characters(css_class):
#     return css_class is not None and len(css_class) == 7
#
# for tag in soup.find_all(class_=has_seven_characters):
# 	print(tag['id'])

# demo 8 因为 CSS 可以有多个值，所以我们可以分别搜索 CSS 中的每个值。
# css_soup = BeautifulSoup('<p class="body strikeout"></p>', 'lxml')
# tags = css_soup.find_all("p", class_="strikeout")
# print(tags)

# demo 9 不仅可以按照标签和 CSS 来搜索整个文档，还可以使用 text 来按照内容来搜索。同时 text 还可以配合其他属性一起来完成搜索任务。
# tags = soup.find_all(text="Google")
# print("google : ", tags)
#
# tags = soup.find_all(text=["Baidu", "Bing"])
# print("baidu & bing : ", tags)
#
# tags = soup.find_all('a', text="Google")
# print("a[text=google] : ", tags)

# demo 10 限制返回数量
# tag = soup.find_all("a", limit=1)
# print(tag)
#
# tags = soup.find_all("p", recursive=False)
# print(tags)
'''
find() 函数
该函数只会返回一个结果，与 find_all(some_args, limit=1) 是等价的，唯一的区别就是该函数直接返回结果，
而 find_all() 函数返回包含一个结果的列表。另外 find_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None。除此之外使用上没有其他差别。

其他函数
除了 find_all() 和 find() 外，Beautiful Soup 中还有 10 个用于搜索的 API，其中中五个用的是与 find_all() 相同的搜索参数，另外 5 个与 find() 方法的搜索参数类似，区别仅是它们搜索文档的范围不同。

find_parents() 和 find_parent() 用来搜索当前节点的父节点。

find_next_siblings() 和 find_next_sibling() 对在当前节点后面解析的所有兄弟节点进行迭代。

find_previous_siblings() 和 find_previous_sibling() 对在当前节点前面解析的所有兄弟节点进行迭代。

find_all_next() 和 find_next() 对当前节点之后的 TAG 和字符串进行迭代。

find_all_previous() 和 find_previous() 对当前节点之前的 TAG 和字符串进行迭代。

以上五组函数的区别仅仅是前者返回一个所有符合搜索条件的节点列表，而后者只返回第一个符合搜索条件的节点。

因为这 10 个 API 的使用和 find_all() 与 find() 大同小异，所有i这里不在举例，读者可以自己探索。
'''

# TODO  CSS 选择器
# demo 11 通过某个标签逐层查找。
# tags = soup.select("body a")
# for tag in tags:
# 	print(tag['href'])

# demo 12 查找某个标签下的直接子标签
# tags = soup.select("p > a")
# print(tags)
#
# tags = soup.select("p > #google")
# print(tags)

# demo 13
# tags = soup.select(".website")
# for tag in tags:
# 	print(tag.string)

# demo 14 通过标签的 id 属性查找
# tags = soup.select("#google")
# print(tags)

# demo 15 通过属性的值来查找
# tags = soup.select('a[href="https://cn.bing.com"]')
# print(tags)

# 详细api文档： https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#
